const std = @import("std");
const text_buffer = @import("../text-buffer.zig");
const text_buffer_view = @import("../text-buffer-view.zig");
const buffer = @import("../buffer.zig");
const gp = @import("../grapheme.zig");
const ss = @import("../syntax-style.zig");

const TextBuffer = text_buffer.TextBuffer;
const TextBufferView = text_buffer_view.TextBufferView;
const OptimizedBuffer = buffer.OptimizedBuffer;
const RGBA = text_buffer.RGBA;
const WrapMode = text_buffer.WrapMode;
const StyledChunk = text_buffer.StyledChunk;

test "drawTextBuffer - simple single line text" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Hello World");

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var out_buffer: [100]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    try std.testing.expect(std.mem.startsWith(u8, result, "Hello World"));
}

test "drawTextBuffer - empty text buffer" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("");

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
}

test "drawTextBuffer - multiple lines without wrapping" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 1\nLine 2\nLine 3");

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len == 3);
}

test "drawTextBuffer - text wrapping at word boundaries" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("This is a long line that should wrap at word boundaries");
    view.setWrapMode(.word);
    view.setWrapWidth(15);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        15,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len > 1);
}

test "drawTextBuffer - text wrapping at character boundaries" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    view.setWrapMode(.char);
    view.setWrapWidth(10);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        10,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len == 4);
}

test "drawTextBuffer - no wrapping with none mode" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("This is a very long line that extends beyond the buffer width");
    view.setWrapMode(.word);
    view.setWrapWidth(null);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len == 1);
}

test "drawTextBuffer - wrapped text with multiple lines" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("First long line that wraps\nSecond long line that also wraps\nThird line");
    view.setWrapMode(.word);
    view.setWrapWidth(15);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        15,
        15,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len >= 3);
}

test "drawTextBuffer - unicode characters with wrapping" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Hello 世界 🌟 Test wrapping");
    view.setWrapMode(.word);
    view.setWrapWidth(15);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        15,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len > 0);
}

test "drawTextBuffer - wrapping preserves wide characters" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("測試測試測試測試測試");
    view.setWrapMode(.char);
    view.setWrapWidth(10);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        10,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len > 1);
}

test "drawTextBuffer - wrapped text with offset position" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Short line that wraps nicely");
    view.setWrapMode(.word);
    view.setWrapWidth(10);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        20,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 5, 5);

    const cell = opt_buffer.get(5, 5);
    try std.testing.expect(cell != null);
    try std.testing.expect(cell.?.char != 32);
}

test "drawTextBuffer - clipping with scrolled view" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 1\nLine 2\nLine 3\nLine 4");

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len >= 4);
}

test "drawTextBuffer - wrapping with very narrow width" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Hello");
    view.setWrapMode(.char);
    view.setWrapWidth(3);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        3,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len == 2);
}

test "drawTextBuffer - word wrap doesn't break mid-word" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Hello World");
    view.setWrapMode(.word);
    view.setWrapWidth(8);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        8,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len == 2);
}

test "drawTextBuffer - empty lines render correctly" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 1\n\nLine 3");

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len == 3);
}

test "drawTextBuffer - wrapping with tabs" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Hello\tWorld\tTest");
    view.setWrapMode(.word);
    view.setWrapWidth(15);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        15,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
}

test "drawTextBuffer - very long unwrapped line clipping" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    var long_text = std.ArrayList(u8).init(std.testing.allocator);
    defer long_text.deinit();
    try long_text.appendNTimes('A', 200);

    try tb.setText(long_text.items);
    view.setWrapMode(.word);
    view.setWrapWidth(null);

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len == 1);
}

test "drawTextBuffer - wrap mode transitions" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("This is a test line for wrapping");

    view.setWrapMode(.word);
    view.setWrapWidth(null);
    view.updateVirtualLines();
    const no_wrap_lines = view.getVirtualLines().len;

    view.setWrapMode(.char);
    view.setWrapWidth(10);
    view.updateVirtualLines();
    const char_lines = view.getVirtualLines().len;

    view.setWrapMode(.word);
    view.setWrapWidth(10);
    view.updateVirtualLines();
    const word_lines = view.getVirtualLines().len;

    try std.testing.expect(no_wrap_lines == 1);
    try std.testing.expect(char_lines > 1);
    try std.testing.expect(word_lines > 1);
}

test "drawTextBuffer - changing wrap width updates virtual lines" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("AAAAAAAAAAAAAAAAAAAAAAAAAAAA");

    view.setWrapMode(.char);
    view.setWrapWidth(10);
    view.updateVirtualLines();
    const lines_10 = view.getVirtualLines().len;

    view.setWrapWidth(20);
    view.updateVirtualLines();
    const lines_20 = view.getVirtualLines().len;

    view.setWrapWidth(5);
    view.updateVirtualLines();
    const lines_5 = view.getVirtualLines().len;

    try std.testing.expect(lines_10 > lines_20);
    try std.testing.expect(lines_5 > lines_10);
}

test "drawTextBuffer - wrapping with mixed ASCII and Unicode" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("ABC測試DEF試験GHI");
    view.setWrapMode(.char);
    view.setWrapWidth(10);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        10,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len > 1);
}

test "setStyledText - basic rendering with single chunk" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    const style = try ss.SyntaxStyle.init(std.testing.allocator);
    defer style.deinit();
    tb.setSyntaxStyle(style);

    const text = "Hello World";
    const fg_color = [4]f32{ 1.0, 1.0, 1.0, 1.0 };

    const chunks = [_]StyledChunk{.{
        .text_ptr = text.ptr,
        .text_len = text.len,
        .fg_ptr = @ptrCast(&fg_color),
        .bg_ptr = null,
        .attributes = 0,
    }};

    try tb.setStyledText(&chunks);

    var out_buffer: [100]u8 = undefined;
    const written = tb.getPlainTextIntoBuffer(&out_buffer);
    const result = out_buffer[0..written];

    try std.testing.expectEqualStrings("Hello World", result);
}

test "setStyledText - multiple chunks render correctly" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    const style = try ss.SyntaxStyle.init(std.testing.allocator);
    defer style.deinit();
    tb.setSyntaxStyle(style);

    const text0 = "Hello ";
    const text1 = "World";
    const fg_color = [4]f32{ 1.0, 1.0, 1.0, 1.0 };

    const chunks = [_]StyledChunk{
        .{ .text_ptr = text0.ptr, .text_len = text0.len, .fg_ptr = @ptrCast(&fg_color), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = text1.ptr, .text_len = text1.len, .fg_ptr = @ptrCast(&fg_color), .bg_ptr = null, .attributes = 0 },
    };

    try tb.setStyledText(&chunks);

    var out_buffer: [100]u8 = undefined;
    const written = tb.getPlainTextIntoBuffer(&out_buffer);
    const result = out_buffer[0..written];

    try std.testing.expectEqualStrings("Hello World", result);
}

// Viewport Tests

test "viewport - basic vertical scrolling limits returned lines" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9");

    view.setViewport(.{ .x = 0, .y = 2, .width = 20, .height = 5 });

    const visible_lines = view.getVirtualLines();

    try std.testing.expectEqual(@as(usize, 5), visible_lines.len);
    try std.testing.expectEqual(@as(usize, 2), visible_lines[0].source_line);
    try std.testing.expectEqual(@as(usize, 6), visible_lines[4].source_line);
}

test "viewport - vertical scrolling at start boundary" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4");

    view.setViewport(.{ .x = 0, .y = 0, .width = 20, .height = 3 });

    const visible_lines = view.getVirtualLines();

    try std.testing.expectEqual(@as(usize, 3), visible_lines.len);
    try std.testing.expectEqual(@as(usize, 0), visible_lines[0].source_line);
    try std.testing.expectEqual(@as(usize, 2), visible_lines[2].source_line);
}

test "viewport - vertical scrolling at end boundary" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4");

    view.setViewport(.{ .x = 0, .y = 3, .width = 20, .height = 3 });

    const visible_lines = view.getVirtualLines();

    try std.testing.expectEqual(@as(usize, 2), visible_lines.len);
    try std.testing.expectEqual(@as(usize, 3), visible_lines[0].source_line);
    try std.testing.expectEqual(@as(usize, 4), visible_lines[1].source_line);
}

test "viewport - vertical scrolling beyond content" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2");

    view.setViewport(.{ .x = 0, .y = 10, .width = 20, .height = 5 });

    const visible_lines = view.getVirtualLines();

    try std.testing.expectEqual(@as(usize, 0), visible_lines.len);
}

test "viewport - with wrapping vertical scrolling" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("This is a long line that will wrap\nShort\nAnother long line that wraps");

    view.setWrapMode(.word);
    view.setWrapWidth(15);
    view.updateVirtualLines();

    const total_vlines = view.getVirtualLineCount();
    try std.testing.expect(total_vlines > 3);

    view.setViewport(.{ .x = 0, .y = 2, .width = 15, .height = 3 });

    const visible_lines = view.getVirtualLines();

    try std.testing.expectEqual(@as(usize, 3), visible_lines.len);
}

test "viewport - getCachedLineInfo returns only viewport lines" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5");

    view.setViewport(.{ .x = 0, .y = 1, .width = 20, .height = 3 });

    const line_info = view.getCachedLineInfo();

    try std.testing.expectEqual(@as(usize, 3), line_info.starts.len);
    try std.testing.expectEqual(@as(usize, 3), line_info.widths.len);
}

test "viewport - changing viewport updates returned lines" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5");

    view.setViewport(.{ .x = 0, .y = 0, .width = 20, .height = 2 });
    const lines1 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), lines1.len);
    try std.testing.expectEqual(@as(usize, 0), lines1[0].source_line);

    view.setViewport(.{ .x = 0, .y = 3, .width = 20, .height = 2 });
    const lines2 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), lines2.len);
    try std.testing.expectEqual(@as(usize, 3), lines2[0].source_line);

    view.setViewport(.{ .x = 0, .y = 1, .width = 20, .height = 4 });
    const lines3 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 4), lines3.len);
    try std.testing.expectEqual(@as(usize, 1), lines3[0].source_line);
}

test "viewport - null viewport returns all lines" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4");

    const all_lines = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 5), all_lines.len);

    view.setViewport(.{ .x = 0, .y = 1, .width = 20, .height = 2 });
    const viewport_lines = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), viewport_lines.len);

    view.setViewport(null);
    const all_lines_again = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 5), all_lines_again.len);
}

test "viewport - setViewportSize convenience method" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2\nLine 3");

    view.setViewportSize(20, 2);
    const vp1 = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 0), vp1.x);
    try std.testing.expectEqual(@as(u32, 0), vp1.y);
    try std.testing.expectEqual(@as(u32, 20), vp1.width);
    try std.testing.expectEqual(@as(u32, 2), vp1.height);

    view.setViewport(.{ .x = 5, .y = 1, .width = 20, .height = 2 });

    view.setViewportSize(30, 3);
    const vp2 = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 5), vp2.x);
    try std.testing.expectEqual(@as(u32, 1), vp2.y);
    try std.testing.expectEqual(@as(u32, 30), vp2.width);
    try std.testing.expectEqual(@as(u32, 3), vp2.height);
}

test "viewport - stores horizontal offset value with no wrapping" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");

    view.setWrapMode(.none);
    view.setWrapWidth(null);

    view.setViewport(.{ .x = 5, .y = 0, .width = 10, .height = 1 });

    const vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 5), vp.x);
    try std.testing.expectEqual(@as(u32, 0), vp.y);
    try std.testing.expectEqual(@as(u32, 10), vp.width);
    try std.testing.expectEqual(@as(u32, 1), vp.height);

    const lines = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 1), lines.len);
}

test "viewport - preserves horizontal offset when changing vertical (no wrap)" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("ABCDEFGHIJ\nKLMNOPQRST\nUVWXYZ1234");

    view.setWrapMode(.none);
    view.setWrapWidth(null);

    view.setViewport(.{ .x = 3, .y = 0, .width = 8, .height = 2 });

    var vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 3), vp.x);
    try std.testing.expectEqual(@as(u32, 0), vp.y);

    view.setViewport(.{ .x = 3, .y = 1, .width = 8, .height = 2 });

    vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 3), vp.x);
    try std.testing.expectEqual(@as(u32, 1), vp.y);

    const visible_lines = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), visible_lines.len);
    try std.testing.expectEqual(@as(usize, 1), visible_lines[0].source_line);
}

test "viewport - can set large horizontal offset (no wrap)" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Short\nLonger line here\nTiny");

    view.setWrapMode(.none);
    view.setWrapWidth(null);

    view.setViewport(.{ .x = 10, .y = 0, .width = 10, .height = 3 });

    const vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 10), vp.x);

    const visible_lines = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 3), visible_lines.len);
}

test "viewport - horizontal and vertical offset combined (no wrap)" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0: ABCDEFGHIJ\nLine 1: KLMNOPQRST\nLine 2: UVWXYZ1234\nLine 3: 567890ABCD");

    view.setWrapMode(.none);
    view.setWrapWidth(null);

    view.setViewport(.{ .x = 8, .y = 1, .width = 15, .height = 2 });

    const vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 8), vp.x);
    try std.testing.expectEqual(@as(u32, 1), vp.y);

    const visible_lines = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), visible_lines.len);
    try std.testing.expectEqual(@as(usize, 1), visible_lines[0].source_line);
    try std.testing.expectEqual(@as(usize, 2), visible_lines[1].source_line);
}

test "viewport - horizontal scrolling only for no-wrap mode" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const long_text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    try tb.setText(long_text);

    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 10, .y = 0, .width = 15, .height = 1 });
    view.updateVirtualLines();

    var vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 10), vp.x);

    var lines = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 1), lines.len);

    view.setWrapMode(.char);
    view.setViewport(.{ .x = 10, .y = 0, .width = 15, .height = 5 });
    view.updateVirtualLines();

    vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 10), vp.x);

    lines = view.getVirtualLines();
    try std.testing.expect(lines.len > 1);
}

test "viewport - horizontal offset irrelevant with wrapping enabled" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("This is a very long line that will wrap into multiple virtual lines");

    view.setWrapMode(.word);
    view.setWrapWidth(20);
    view.updateVirtualLines();

    const total_vlines = view.getVirtualLineCount();
    try std.testing.expect(total_vlines > 1);

    view.setViewport(.{ .x = 5, .y = 1, .width = 15, .height = 2 });

    const vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 5), vp.x);
    try std.testing.expectEqual(@as(u32, 1), vp.y);
    try std.testing.expectEqual(@as(u32, 15), vp.width);

    const visible_lines = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), visible_lines.len);
}

test "viewport - zero width or height" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line 0\nLine 1\nLine 2");

    view.setViewport(.{ .x = 0, .y = 0, .width = 20, .height = 0 });
    const lines1 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 0), lines1.len);

    view.setViewport(.{ .x = 0, .y = 0, .width = 0, .height = 2 });
    const lines2 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), lines2.len);
}

test "viewport - viewport sets wrap width automatically" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD");

    view.setWrapMode(.char);

    view.setViewport(.{ .x = 0, .y = 0, .width = 10, .height = 5 });
    view.updateVirtualLines();

    const vline_count_10 = view.getVirtualLineCount();

    view.setViewport(.{ .x = 0, .y = 0, .width = 20, .height = 5 });
    view.updateVirtualLines();

    const vline_count_20 = view.getVirtualLineCount();

    try std.testing.expect(vline_count_10 > vline_count_20);
}

test "viewport - moving viewport dynamically (no wrap)" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("0123456789\nABCDEFGHIJ\nKLMNOPQRST\nUVWXYZ!@#$");

    view.setWrapMode(.none);
    view.setWrapWidth(null);

    view.setViewport(.{ .x = 0, .y = 0, .width = 5, .height = 2 });
    var vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 0), vp.x);
    try std.testing.expectEqual(@as(u32, 0), vp.y);
    const lines1 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), lines1.len);
    try std.testing.expectEqual(@as(usize, 0), lines1[0].source_line);

    view.setViewport(.{ .x = 0, .y = 1, .width = 5, .height = 2 });
    vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 0), vp.x);
    try std.testing.expectEqual(@as(u32, 1), vp.y);
    const lines2 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), lines2.len);
    try std.testing.expectEqual(@as(usize, 1), lines2[0].source_line);

    view.setViewport(.{ .x = 3, .y = 1, .width = 5, .height = 2 });
    vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 3), vp.x);
    try std.testing.expectEqual(@as(u32, 1), vp.y);
    const lines3 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), lines3.len);

    view.setViewport(.{ .x = 5, .y = 2, .width = 5, .height = 2 });
    vp = view.getViewport().?;
    try std.testing.expectEqual(@as(u32, 5), vp.x);
    try std.testing.expectEqual(@as(u32, 2), vp.y);
    const lines4 = view.getVirtualLines();
    try std.testing.expectEqual(@as(usize, 2), lines4.len);
    try std.testing.expectEqual(@as(usize, 2), lines4[0].source_line);
}

test "loadFile - loads and renders file correctly" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const test_content = "ABC\nDEF";
    const tmpdir = std.testing.tmpDir(.{});
    var tmp = tmpdir;
    defer tmp.cleanup();

    const file = try tmp.dir.createFile("test.txt", .{});
    try file.writeAll(test_content);
    file.close();

    const dir_path = try tmp.dir.realpathAlloc(std.testing.allocator, ".");
    defer std.testing.allocator.free(dir_path);

    const file_path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ dir_path, "test.txt" });
    defer std.testing.allocator.free(file_path);

    try tb.loadFile(file_path);

    const line_count = tb.getLineCount();
    try std.testing.expectEqual(@as(u32, 2), line_count);

    const char_count = tb.getLength();
    try std.testing.expectEqual(@as(u32, 6), char_count);

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var render_buffer: [200]u8 = undefined;
    const render_written = try opt_buffer.writeResolvedChars(&render_buffer, false);
    const render_result = render_buffer[0..render_written];

    try std.testing.expect(std.mem.startsWith(u8, render_result, "ABC"));
}

test "drawTextBuffer - horizontal viewport offset renders correctly without wrapping" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("0123456789ABCDEFGHIJ");

    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 5, .y = 0, .width = 10, .height = 1 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        10,
        1,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var out_buffer: [100]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    try std.testing.expect(std.mem.startsWith(u8, result, "56789ABCDE"));
}

test "drawTextBuffer - horizontal viewport offset with multiple lines" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("ABCDEFGHIJKLMNO\n0123456789!@#$%\nXYZ[\\]^_`{|}~");

    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 3, .y = 0, .width = 8, .height = 3 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        8,
        3,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var out_buffer: [100]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    try std.testing.expect(std.mem.indexOf(u8, result, "DEFGHIJK") != null);
    try std.testing.expect(std.mem.indexOf(u8, result, "3456789!") != null);
    try std.testing.expect(std.mem.indexOf(u8, result, "[\\]^_`{|") != null);
}

test "drawTextBuffer - combined horizontal and vertical viewport offsets" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("Line0ABCDEFGHIJ\nLine1KLMNOPQRST\nLine2UVWXYZ0123\nLine3456789!@#$");

    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 5, .y = 1, .width = 10, .height = 2 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        10,
        2,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var out_buffer: [100]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    try std.testing.expect(std.mem.indexOf(u8, result, "KLMNOPQRST") != null);
    try std.testing.expect(std.mem.indexOf(u8, result, "UVWXYZ0123") != null);
}

test "drawTextBuffer - horizontal viewport stops rendering at viewport width" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");

    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 5, .y = 0, .width = 10, .height = 1 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        10,
        1,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var out_buffer: [100]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    try std.testing.expectEqualStrings("56789ABCDE", result[0..10]);

    const cell_9 = opt_buffer.get(9, 0);
    try std.testing.expect(cell_9 != null);
    try std.testing.expectEqual(@as(u32, 'E'), cell_9.?.char);
}

test "drawTextBuffer - horizontal viewport with small buffer renders only viewport width" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");

    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 10, .y = 0, .width = 5, .height = 1 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        1,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const cell_0 = opt_buffer.get(0, 0);
    try std.testing.expect(cell_0 != null);
    try std.testing.expectEqual(@as(u32, 'K'), cell_0.?.char);

    const cell_4 = opt_buffer.get(4, 0);
    try std.testing.expect(cell_4 != null);
    try std.testing.expectEqual(@as(u32, 'O'), cell_4.?.char);

    const cell_5 = opt_buffer.get(5, 0);
    try std.testing.expect(cell_5 != null);
    try std.testing.expectEqual(@as(u32, 32), cell_5.?.char);

    const cell_6 = opt_buffer.get(6, 0);
    try std.testing.expect(cell_6 != null);
    try std.testing.expectEqual(@as(u32, 32), cell_6.?.char);
}

test "drawTextBuffer - horizontal viewport width limits rendering (efficiency test)" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    var long_line = std.ArrayList(u8).init(std.testing.allocator);
    defer long_line.deinit();
    try long_line.appendNTimes('A', 1000);

    try tb.setText(long_line.items);

    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 100, .y = 0, .width = 10, .height = 1 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        50,
        1,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var non_space_count: u32 = 0;
    var i: u32 = 0;
    while (i < 50) : (i += 1) {
        if (opt_buffer.get(i, 0)) |cell| {
            if (cell.char == 'A') {
                non_space_count += 1;
            }
        }
    }

    try std.testing.expectEqual(@as(u32, 10), non_space_count);
}

test "drawTextBuffer - overwriting wide grapheme with ASCII leaves no ghost chars" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try tb.setText("世界");
    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const first_cell = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(first_cell.char));
    try std.testing.expectEqual(@as(u32, 2), gp.encodedCharWidth(first_cell.char));

    const second_cell = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expect(gp.isContinuationChar(second_cell.char));

    try tb.setText("ABC");
    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const cell_a = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'A'), cell_a.char);
    try std.testing.expect(!gp.isGraphemeChar(cell_a.char));
    try std.testing.expect(!gp.isContinuationChar(cell_a.char));

    const cell_b = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'B'), cell_b.char);
    try std.testing.expect(!gp.isGraphemeChar(cell_b.char));
    try std.testing.expect(!gp.isContinuationChar(cell_b.char));

    const cell_c = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'C'), cell_c.char);
    try std.testing.expect(!gp.isGraphemeChar(cell_c.char));
    try std.testing.expect(!gp.isContinuationChar(cell_c.char));

    var out_buffer: [100]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];
    try std.testing.expect(std.mem.startsWith(u8, result, "ABC"));
}

test "drawTextBuffer - syntax style destroy does not crash" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    var style = try ss.SyntaxStyle.init(std.testing.allocator);
    tb.setSyntaxStyle(style);

    const style_id = try style.registerStyle("test", .{ 1.0, 0.0, 0.0, 1.0 }, null, 0);
    try tb.setText("Hello World");
    try tb.addHighlightByCharRange(0, 5, style_id, 1, 0);

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var out_buffer: [100]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];
    try std.testing.expect(std.mem.startsWith(u8, result, "Hello World"));

    style.deinit();

    try std.testing.expect(tb.getSyntaxStyle() == null);

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const written2 = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result2 = out_buffer[0..written2];
    try std.testing.expect(std.mem.startsWith(u8, result2, "Hello World"));
}

test "drawTextBuffer - tabs are rendered as spaces (empty cells)" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    tb.setTabWidth(4);

    try tb.setText("A\tB");

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'A'), cell_0.char);

    const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_1.char);

    const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_2.char);

    const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_3.char);

    const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_4.char);

    // With static tabs: A at col 0, tab takes 4 cols (1-4), B at col 5
    const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'B'), cell_5.char);
}

test "drawTextBuffer - tab indicator renders with correct color" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    tb.setTabWidth(4);
    try tb.setText("A\tB");

    view.setTabIndicator(@as(u32, '→'));
    view.setTabIndicatorColor(RGBA{ 0.25, 0.25, 0.25, 1.0 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'A'), cell_0.char);

    const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '→'), cell_1.char);
    try std.testing.expectEqual(@as(f32, 0.25), cell_1.fg[0]);
    try std.testing.expectEqual(@as(f32, 0.25), cell_1.fg[1]);
    try std.testing.expectEqual(@as(f32, 0.25), cell_1.fg[2]);

    const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_2.char);

    const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_3.char);

    const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_4.char);

    // With static tabs: A at col 0, tab takes 4 cols (1-4), B at col 5
    const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'B'), cell_5.char);
}

test "drawTextBuffer - tab without indicator renders as spaces" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    tb.setTabWidth(4);
    try tb.setText("A\tB");

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        20,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'A'), cell_0.char);

    const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_1.char);

    const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_2.char);

    const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_3.char);

    const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 32), cell_4.char);

    // With static tabs: A at col 0, tab takes 4 cols (1-4), B at col 5
    const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'B'), cell_5.char);
}

test "drawTextBuffer - mixed ASCII and Unicode with emoji renders completely" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("- ✅ All 881 native tests passs");

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        50,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '-'), cell_0.char);

    const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_1.char);

    const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_2.char));
    const width_2 = gp.encodedCharWidth(cell_2.char);
    try std.testing.expectEqual(@as(u32, 2), width_2);

    const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
    try std.testing.expect(gp.isContinuationChar(cell_3.char));

    const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_4.char);

    const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'A'), cell_5.char);

    const cell_6 = opt_buffer.get(6, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'l'), cell_6.char);

    const cell_7 = opt_buffer.get(7, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'l'), cell_7.char);

    const cell_8 = opt_buffer.get(8, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_8.char);

    const cell_9 = opt_buffer.get(9, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '8'), cell_9.char);

    const cell_10 = opt_buffer.get(10, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '8'), cell_10.char);

    const cell_11 = opt_buffer.get(11, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '1'), cell_11.char);

    const cell_12 = opt_buffer.get(12, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_12.char);

    const cell_13 = opt_buffer.get(13, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'n'), cell_13.char);

    const cell_14 = opt_buffer.get(14, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'a'), cell_14.char);

    const cell_15 = opt_buffer.get(15, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 't'), cell_15.char);

    const cell_16 = opt_buffer.get(16, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'i'), cell_16.char);

    const cell_17 = opt_buffer.get(17, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'v'), cell_17.char);

    const cell_18 = opt_buffer.get(18, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'e'), cell_18.char);

    const cell_19 = opt_buffer.get(19, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_19.char);

    const cell_20 = opt_buffer.get(20, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 't'), cell_20.char);

    const cell_21 = opt_buffer.get(21, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'e'), cell_21.char);

    const cell_22 = opt_buffer.get(22, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 's'), cell_22.char);

    const cell_23 = opt_buffer.get(23, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 't'), cell_23.char);

    const cell_24 = opt_buffer.get(24, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 's'), cell_24.char);

    const cell_25 = opt_buffer.get(25, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_25.char);

    const cell_26 = opt_buffer.get(26, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'p'), cell_26.char);

    const cell_27 = opt_buffer.get(27, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'a'), cell_27.char);

    const cell_28 = opt_buffer.get(28, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 's'), cell_28.char);

    const cell_29 = opt_buffer.get(29, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 's'), cell_29.char);

    const cell_30 = opt_buffer.get(30, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 's'), cell_30.char);

    var out_buffer: [500]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    try std.testing.expect(std.mem.indexOf(u8, result, "- ✅ All 881 native tests passs") != null);

    const plain_text = tb.getPlainTextIntoBuffer(&out_buffer);
    const plain_result = out_buffer[0..plain_text];
    try std.testing.expectEqualStrings("- ✅ All 881 native tests passs", plain_result);
}

test "viewport width = 31 exactly - last character rendering" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("- ✅ All 881 native tests passs");

    // Set viewport width to EXACTLY 31 (the display width needed)
    view.setViewport(text_buffer_view.Viewport{ .x = 0, .y = 0, .width = 31, .height = 1 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        50,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // BUG CHECK: The last 's' at cell 30 should be present
    const cell_30 = opt_buffer.get(30, 0);
    if (cell_30) |c| {
        try std.testing.expectEqual(@as(u32, 's'), c.char);
    } else {
        return error.TestFailed;
    }
}

test "drawTextBuffer - complex multilingual text with diverse scripts and emojis" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const text =
        \\# The Celestial Journey of संस्कृति 🌟🔮✨
        \\In the beginning, there was नमस्ते 🙏 and the ancient wisdom of the ॐ symbol echoing through dimensions. The travelers 🧑‍🚀👨‍🚀👩‍🚀 embarked on their quest through the cosmos, guided by the mysterious རྒྱ་མཚོ and the luminous 🌈🦄🧚‍♀️ beings of light. They encountered the great देवनागरी scribes who wrote in flowing अक्षर characters, documenting everything in their sacred texts 📜📖✍️.
        \\## Chapter प्रथम: The Eastern Gardens 🏯🎋🌸
        \\The journey led them to the mystical lands where 漢字 (kanji) danced with ひらがな and カタカナ across ancient scrolls 📯🎴🎎. In the gardens of Seoul, they found 한글 inscriptions speaking of 사랑 (love) and 평화 (peace) 💝🕊️☮️. The monks meditated under the bodhi tree 🧘‍♂️🌳, contemplating the nature of धर्म while drinking matcha 🍵 and eating 餃子 dumplings 🥟.
        \\Strange creatures emerged from the mist: 🦥🦦🦧🦨🦩🦚🦜🦝🦞🦟. They spoke in riddles about the प्राचीन (ancient) ways and the नवीन (new) paths forward. "भविष्य में क्या है?" they asked, while the ໂຫຍ່າກເຈົ້າ whispered secrets in Lao script 🤫🗣️💬.
        \\## The संगम (Confluence) of Scripts 🌊📝🎭
        \\At the great confluence, they witnessed the merger of བོད་ཡིག (Tibetan), ગુજરાતી (Gujarati), and தமிழ் (Tamil) scripts flowing together like rivers 🏞️🌊💧. The scholars debated about ਪੰਜਾਬੀ philosophy while juggling 🤹‍♂️🎪🎨 colorful orbs that represented different తెలుగు concepts.
        \\The marketplace buzzed with activity 🏪🛒💰: merchants sold বাংলা spices 🌶️🧄🧅, ಕನ್ನಡ silks 🧵👘, and മലയാളം handicrafts 🎨🖼️. Children played with toys shaped like 🦖🦕🐉🐲 while their parents bargained using ancient ଓଡ଼ିଆ numerals and gestures 🤝🤲👐.
        \\## The Festival of ๑๐๐ Lanterns 🏮🎆🎇
        \\During the grand festival, they lit exactly ๑๐๐ (100 in Thai numerals) lanterns 🏮🕯️💡 that floated into the night sky like ascending ความหวัง (hopes). The celebration featured dancers 💃🕺🩰 performing classical moves from भरतनाट्यम tradition, their मुद्रा hand gestures telling stories of प्रेम and वीरता.
        \\Musicians played unusual instruments: the 🎻🎺🎷🎸🪕🪘 ensemble created harmonies that resonated with the वेद chants and མཆོད་རྟེན bells 🔔⛩️. The audience sat mesmerized 😵‍💫🤯✨, some sipping on bubble tea 🧋 while others enjoyed मिठाई sweets 🍬🍭🧁.
        \\## The འཕྲུལ་དེབ (Machine) Age Arrives ⚙️🤖🦾
        \\As modernity crept in, the ancient འཁོར་ལོ (wheel) gave way to 🚗🚕🚙🚌🚎 vehicles and eventually to 🚀🛸🛰️ spacecraft. The યુવાન (youth) learned to code in Python 🐍💻⌨️, but still honored their గురువు (teachers) who taught them the old ways of ज्ञान acquisition 🧠📚🎓.
        \\The সমাজ (society) transformed: robots 🤖🦾🦿 worked alongside humans 👨‍💼👩‍💼👨‍🔬👩‍🔬, and AI learned to read སྐད (languages) from across the planet 🌍🌎🌏. Yet somehow, the essence of मानवता remained intact, preserved in the கவிதை (poetry) and the ກາບແກ້ວ stories passed down through generations 👴👵👨‍👩‍👧‍👦.
        \\## The Final ಅಧ್ಯಾಯ (Chapter) 🌅🌄🌠
        \\As the sun set over the പർവ്വതങ്ങൾ (mountains) 🏔️⛰️🗻, our travelers realized that every script, every symbol—from ا to ㄱ to অ to अ—represented not just sounds, but entire civilizations' worth of विचार (thoughts) and ಕನಸು (dreams) 💭💤🌌.
        \\They gathered around the final campfire 🔥🏕️, sharing stories in ภาษา (languages) both ancient and new. Someone brought out a guitar 🎸 and started singing in ગીત form, while others prepared ආහාර (food) 🍛🍲🥘 seasoned with love ❤️💕💖 and memories 📸🎞️📹.
        \\And so they learned that whether written in দেবনাগরী, 中文, 한글, or ไทย, the human experience transcends boundaries 🌐🤝🌈. The weird emojis 🦩🧿🪬🫀🫁🧠 and complex scripts were all part of the same beautiful བསྟན་པ (teaching): that diversity is our greatest strength 💪✊🙌.
        \\The end. समाप्त. 끝. จบ. முடிவு. ముగింపు. সমাপ্তি. ഒടുക്കം. ಅಂತ್ಯ. અંત. 🎬🎭🎪✨🌟⭐
        \\
    ;

    try tb.setText(text);

    // Test with word wrapping
    view.setWrapMode(.word);
    view.setWrapWidth(80);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        80,
        100,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // Verify the text buffer can handle complex multilingual content
    const virtual_lines = view.getVirtualLines();
    try std.testing.expect(virtual_lines.len > 0);

    // Test that we can get the plain text back
    var plain_buffer: [10000]u8 = undefined;
    const plain_len = tb.getPlainTextIntoBuffer(&plain_buffer);
    const plain_text = plain_buffer[0..plain_len];

    // Verify some key multilingual content is present
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "संस्कृति") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "नमस्ते") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "漢字") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "한글") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "தமிழ்") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "বাংলা") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "ಕನ್ನಡ") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "മലയാളം") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "🌟") != null);
    try std.testing.expect(std.mem.indexOf(u8, plain_text, "🙏") != null);

    // Test with no wrapping
    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.updateVirtualLines();

    const no_wrap_lines = view.getVirtualLines();
    // Should have one line per actual newline in the text
    try std.testing.expect(no_wrap_lines.len > 10);

    // Test with character wrapping on narrow width
    view.setWrapMode(.char);
    view.setWrapWidth(40);
    view.updateVirtualLines();

    const char_wrap_lines = view.getVirtualLines();
    // Should wrap into many more lines
    try std.testing.expect(char_wrap_lines.len > virtual_lines.len);

    // Test viewport scrolling through the content
    view.setWrapMode(.word);
    view.setWrapWidth(80);
    view.setViewport(.{ .x = 0, .y = 10, .width = 80, .height = 20 });
    view.updateVirtualLines();

    const viewport_lines = view.getVirtualLines();
    try std.testing.expect(viewport_lines.len <= 20);

    // Verify rendering doesn't crash with complex emoji sequences
    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // Test that line count is reasonable
    const line_count = tb.getLineCount();
    try std.testing.expect(line_count > 15);
}

test "setStyledText - highlight positioning with Unicode text" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const style = try ss.SyntaxStyle.init(std.testing.allocator);
    defer style.deinit();
    tb.setSyntaxStyle(style);

    // Text: "Say नमस्ते please."
    // Layout: "Say " (4 cols) + "नमस्ते" (4 cols) + " " (1 col) + "please" (6 cols) + "." (1 col)
    // We highlight "please" with a green background to verify correct positioning
    const text_part1 = "Say ";
    const text_part2 = "नमस्ते";
    const text_part3 = " ";
    const text_part4 = "please";
    const text_part5 = ".";

    const fg_normal = [4]f32{ 1.0, 1.0, 1.0, 1.0 };
    const bg_highlight = [4]f32{ 0.0, 1.0, 0.0, 1.0 }; // Green background

    const chunks = [_]StyledChunk{
        .{ .text_ptr = text_part1.ptr, .text_len = text_part1.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = text_part2.ptr, .text_len = text_part2.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = text_part3.ptr, .text_len = text_part3.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = text_part4.ptr, .text_len = text_part4.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = @ptrCast(&bg_highlight), .attributes = 0 },
        .{ .text_ptr = text_part5.ptr, .text_len = text_part5.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = null, .attributes = 0 },
    };

    try tb.setStyledText(&chunks);

    // Verify the text content
    var out_buffer: [100]u8 = undefined;
    const written = tb.getPlainTextIntoBuffer(&out_buffer);
    const result = out_buffer[0..written];
    try std.testing.expectEqualStrings("Say नमस्ते please.", result);

    // Calculate expected positions using measureText
    const part1_width = tb.measureText(text_part1);
    const part2_width = tb.measureText(text_part2);
    const part3_width = tb.measureText(text_part3);
    const please_start_col = part1_width + part2_width + part3_width;

    // Render to buffer and check colors
    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        30,
        5,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // Check that "please" (6 characters) all have the green background
    const epsilon: f32 = 0.01;
    var i: u32 = 0;
    while (i < 6) : (i += 1) {
        const cell_col = please_start_col + i;
        const cell = opt_buffer.get(cell_col, 0) orelse return error.TestFailed;

        // Verify green background (R=0, G=1, B=0)
        try std.testing.expect(@abs(cell.bg[0] - 0.0) < epsilon);
        try std.testing.expect(@abs(cell.bg[1] - 1.0) < epsilon);
        try std.testing.expect(@abs(cell.bg[2] - 0.0) < epsilon);
    }

    // Check that text before "please" does NOT have green background
    i = 0;
    while (i < please_start_col) : (i += 1) {
        const cell = opt_buffer.get(i, 0) orelse unreachable;
        const has_green_bg = @abs(cell.bg[1] - 1.0) < epsilon and @abs(cell.bg[0] - 0.0) < epsilon;
        try std.testing.expect(!has_green_bg);
    }

    // Check that "." after "please" does NOT have green background
    const period_col = please_start_col + 6;
    const period_cell = opt_buffer.get(period_col, 0) orelse unreachable;
    const has_green_bg = @abs(period_cell.bg[1] - 1.0) < epsilon and @abs(period_cell.bg[0] - 0.0) < epsilon;
    try std.testing.expect(!has_green_bg);
}

test "drawTextBuffer - multiple syntax highlights with various horizontal viewport offsets" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const style = try ss.SyntaxStyle.init(std.testing.allocator);
    defer style.deinit();
    tb.setSyntaxStyle(style);

    // Register different color styles
    const red_style = try style.registerStyle("red", RGBA{ 1.0, 0.0, 0.0, 1.0 }, null, 0);
    const green_style = try style.registerStyle("green", RGBA{ 0.0, 1.0, 0.0, 1.0 }, null, 0);
    const blue_style = try style.registerStyle("blue", RGBA{ 0.0, 0.0, 1.0, 1.0 }, null, 0);
    const yellow_style = try style.registerStyle("yellow", RGBA{ 1.0, 1.0, 0.0, 1.0 }, null, 0);

    // Text: "const x = function(y) { return y * 2; }"
    const test_text = "const x = function(y) { return y * 2; }";
    // Positions (0-indexed):
    // "const" is at 0-5 (exclusive end, so 0,1,2,3,4)
    // "function" is at 10-18 (chars 10-17)
    // "return" is at 24-30 (chars 24-29)
    // "2" is at 35-36 (char 35)

    try tb.setText(test_text);

    try tb.addHighlightByCharRange(0, 5, red_style, 1, 0); // "const"
    try tb.addHighlightByCharRange(10, 18, green_style, 1, 0); // "function"
    try tb.addHighlightByCharRange(24, 30, blue_style, 1, 0); // "return"
    try tb.addHighlightByCharRange(35, 36, yellow_style, 1, 0); // "2"

    view.setWrapMode(.none);
    view.setWrapWidth(null);

    const epsilon: f32 = 0.01;

    // Test 1: Viewport at x=0 (no scroll)
    {
        view.setViewport(.{ .x = 0, .y = 0, .width = 40, .height = 1 });
        var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 40, 1, .{ .pool = pool, .width_method = .unicode });
        defer opt_buffer.deinit();

        try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
        try opt_buffer.drawTextBuffer(view, 0, 0);

        // Check "const" is red
        const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'c'), cell_0.char);
        try std.testing.expect(@abs(cell_0.fg[0] - 1.0) < epsilon); // Red

        const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 't'), cell_4.char);
        try std.testing.expect(@abs(cell_4.fg[0] - 1.0) < epsilon); // Red

        // Check "function" is green
        const cell_10 = opt_buffer.get(10, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'f'), cell_10.char);
        try std.testing.expect(@abs(cell_10.fg[1] - 1.0) < epsilon); // Green

        const cell_17 = opt_buffer.get(17, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'n'), cell_17.char);
        try std.testing.expect(@abs(cell_17.fg[1] - 1.0) < epsilon); // Green
    }

    // Test 2: Viewport scrolled to x=3 (showing "st x = fun...")
    {
        view.setViewport(.{ .x = 3, .y = 0, .width = 20, .height = 1 });
        var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
        defer opt_buffer.deinit();

        try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
        try opt_buffer.drawTextBuffer(view, 0, 0);

        // Buffer shows characters 3-22 from source: "st x = function(y) {"
        // Position 0: 's' (source 3) - should be RED (part of "const" 0-5)
        // Position 1: 't' (source 4) - should be RED (part of "const" 0-5)
        // Position 2: ' ' (source 5) - NOT red (outside "const")
        // Position 7: 'f' (source 10) - should be GREEN (start of "function" 10-18)
        // Position 14: 'n' (source 17) - should be GREEN (part of "function")

        const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 's'), cell_0.char);
        try std.testing.expect(@abs(cell_0.fg[0] - 1.0) < epsilon); // Red
        try std.testing.expect(@abs(cell_0.fg[1] - 0.0) < epsilon);
        try std.testing.expect(@abs(cell_0.fg[2] - 0.0) < epsilon);

        const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 't'), cell_1.char);
        try std.testing.expect(@abs(cell_1.fg[0] - 1.0) < epsilon); // Red
        try std.testing.expect(@abs(cell_1.fg[1] - 0.0) < epsilon);
        try std.testing.expect(@abs(cell_1.fg[2] - 0.0) < epsilon);

        const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, ' '), cell_2.char);
        try std.testing.expect(@abs(cell_2.fg[0] - 1.0) < epsilon); // White (default)
        try std.testing.expect(@abs(cell_2.fg[1] - 1.0) < epsilon);
        try std.testing.expect(@abs(cell_2.fg[2] - 1.0) < epsilon);

        const cell_7 = opt_buffer.get(7, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'f'), cell_7.char);
        try std.testing.expect(@abs(cell_7.fg[0] - 0.0) < epsilon); // Green
        try std.testing.expect(@abs(cell_7.fg[1] - 1.0) < epsilon);
        try std.testing.expect(@abs(cell_7.fg[2] - 0.0) < epsilon);

        const cell_14 = opt_buffer.get(14, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'n'), cell_14.char);
        try std.testing.expect(@abs(cell_14.fg[0] - 0.0) < epsilon); // Green
        try std.testing.expect(@abs(cell_14.fg[1] - 1.0) < epsilon);
        try std.testing.expect(@abs(cell_14.fg[2] - 0.0) < epsilon);
    }

    // Test 4: Viewport scrolled to x=30 (showing "y * 2; }" based on 40 char text)
    {
        view.setViewport(.{ .x = 30, .y = 0, .width = 20, .height = 1 });
        var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
        defer opt_buffer.deinit();

        try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
        try opt_buffer.drawTextBuffer(view, 0, 0);

        // Actual rendering shows: " y * 2; }"
        // Source chars 30-38 are shown
        // Position 0: ' ' (source 30) - white
        // Position 5: '2' (source 35) - should be YELLOW (highlighted 35-36)

        const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, '2'), cell_5.char);
        try std.testing.expect(@abs(cell_5.fg[0] - 1.0) < epsilon); // Yellow
        try std.testing.expect(@abs(cell_5.fg[1] - 1.0) < epsilon);
        try std.testing.expect(@abs(cell_5.fg[2] - 0.0) < epsilon);
    }
}

test "drawTextBuffer - syntax highlighting with horizontal viewport offset" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const style = try ss.SyntaxStyle.init(std.testing.allocator);
    defer style.deinit();
    tb.setSyntaxStyle(style);

    // Register a red style
    const red_style_id = try style.registerStyle("keyword", RGBA{ 1.0, 0.0, 0.0, 1.0 }, null, 0);

    // Text: "const x = 1"
    // Highlight "const" (characters 0-5) in red
    try tb.setText("const x = 1");
    try tb.addHighlightByCharRange(0, 5, red_style_id, 1, 0);

    // Set viewport to skip first 3 characters, showing "st x = 1"
    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 3, .y = 0, .width = 10, .height = 1 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        10,
        1,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    const epsilon: f32 = 0.01;
    const red_fg = RGBA{ 1.0, 0.0, 0.0, 1.0 };

    // Check that 's' at buffer position 0 is RED
    const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 's'), cell_0.char);
    const is_red_0 = @abs(cell_0.fg[0] - red_fg[0]) < epsilon and
        @abs(cell_0.fg[1] - red_fg[1]) < epsilon and
        @abs(cell_0.fg[2] - red_fg[2]) < epsilon;
    try std.testing.expect(is_red_0);

    // Check that 't' at buffer position 1 is RED
    const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 't'), cell_1.char);
    const is_red_1 = @abs(cell_1.fg[0] - red_fg[0]) < epsilon and
        @abs(cell_1.fg[1] - red_fg[1]) < epsilon and
        @abs(cell_1.fg[2] - red_fg[2]) < epsilon;
    try std.testing.expect(is_red_1);

    // Check that ' ' at buffer position 2 is NOT RED
    const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_2.char);
    const is_red_2 = @abs(cell_2.fg[0] - red_fg[0]) < epsilon and
        @abs(cell_2.fg[1] - red_fg[1]) < epsilon and
        @abs(cell_2.fg[2] - red_fg[2]) < epsilon;
    try std.testing.expect(!is_red_2);

    // Check that 'x' at buffer position 3 is NOT RED
    const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'x'), cell_3.char);
    const is_red_3 = @abs(cell_3.fg[0] - red_fg[0]) < epsilon and
        @abs(cell_3.fg[1] - red_fg[1]) < epsilon and
        @abs(cell_3.fg[2] - red_fg[2]) < epsilon;
    try std.testing.expect(!is_red_3);
}

test "drawTextBuffer - setStyledText with multiple colors and horizontal scrolling" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const style = try ss.SyntaxStyle.init(std.testing.allocator);
    defer style.deinit();
    tb.setSyntaxStyle(style);

    // Simulate what code renderable does with setStyledText
    // Text will be: "const x = function(y) { return y * 2; }"
    // But split into colored chunks like syntax highlighting

    const chunk1_text = "const";
    const chunk2_text = " x = ";
    const chunk3_text = "function";
    const chunk4_text = "(y) { ";
    const chunk5_text = "return";
    const chunk6_text = " y * ";
    const chunk7_text = "2";
    const chunk8_text = "; }";

    const red_color = [4]f32{ 1.0, 0.0, 0.0, 1.0 };
    const white_color = [4]f32{ 1.0, 1.0, 1.0, 1.0 };
    const green_color = [4]f32{ 0.0, 1.0, 0.0, 1.0 };
    const blue_color = [4]f32{ 0.0, 0.0, 1.0, 1.0 };
    const yellow_color = [4]f32{ 1.0, 1.0, 0.0, 1.0 };

    const chunks = [_]StyledChunk{
        .{ .text_ptr = chunk1_text.ptr, .text_len = chunk1_text.len, .fg_ptr = @ptrCast(&red_color), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = chunk2_text.ptr, .text_len = chunk2_text.len, .fg_ptr = @ptrCast(&white_color), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = chunk3_text.ptr, .text_len = chunk3_text.len, .fg_ptr = @ptrCast(&green_color), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = chunk4_text.ptr, .text_len = chunk4_text.len, .fg_ptr = @ptrCast(&white_color), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = chunk5_text.ptr, .text_len = chunk5_text.len, .fg_ptr = @ptrCast(&blue_color), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = chunk6_text.ptr, .text_len = chunk6_text.len, .fg_ptr = @ptrCast(&white_color), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = chunk7_text.ptr, .text_len = chunk7_text.len, .fg_ptr = @ptrCast(&yellow_color), .bg_ptr = null, .attributes = 0 },
        .{ .text_ptr = chunk8_text.ptr, .text_len = chunk8_text.len, .fg_ptr = @ptrCast(&white_color), .bg_ptr = null, .attributes = 0 },
    };

    try tb.setStyledText(&chunks);

    view.setWrapMode(.none);
    view.setWrapWidth(null);

    const epsilon: f32 = 0.01;

    // Helper to check if color matches
    const isRed = struct {
        fn check(fg: RGBA, eps: f32) bool {
            return @abs(fg[0] - 1.0) < eps and @abs(fg[1] - 0.0) < eps and @abs(fg[2] - 0.0) < eps;
        }
    }.check;

    const isGreen = struct {
        fn check(fg: RGBA, eps: f32) bool {
            return @abs(fg[0] - 0.0) < eps and @abs(fg[1] - 1.0) < eps and @abs(fg[2] - 0.0) < eps;
        }
    }.check;

    const isBlue = struct {
        fn check(fg: RGBA, eps: f32) bool {
            return @abs(fg[0] - 0.0) < eps and @abs(fg[1] - 0.0) < eps and @abs(fg[2] - 1.0) < eps;
        }
    }.check;

    const isYellow = struct {
        fn check(fg: RGBA, eps: f32) bool {
            return @abs(fg[0] - 1.0) < eps and @abs(fg[1] - 1.0) < eps and @abs(fg[2] - 0.0) < eps;
        }
    }.check;

    const isWhite = struct {
        fn check(fg: RGBA, eps: f32) bool {
            return @abs(fg[0] - 1.0) < eps and @abs(fg[1] - 1.0) < eps and @abs(fg[2] - 1.0) < eps;
        }
    }.check;

    // Test at x=0 (no scroll)
    {
        view.setViewport(.{ .x = 0, .y = 0, .width = 40, .height = 1 });
        var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 40, 1, .{ .pool = pool, .width_method = .unicode });
        defer opt_buffer.deinit();

        try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
        try opt_buffer.drawTextBuffer(view, 0, 0);

        const cell_0 = opt_buffer.get(0, 0) orelse unreachable; // 'c' from "const"
        try std.testing.expectEqual(@as(u32, 'c'), cell_0.char);
        try std.testing.expect(isRed(cell_0.fg, epsilon));

        const cell_10 = opt_buffer.get(10, 0) orelse unreachable; // 'f' from "function"
        try std.testing.expectEqual(@as(u32, 'f'), cell_10.char);
        try std.testing.expect(isGreen(cell_10.fg, epsilon));
    }

    // Test at x=5 (scrolled past "const")
    {
        view.setViewport(.{ .x = 5, .y = 0, .width = 20, .height = 1 });
        var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
        defer opt_buffer.deinit();

        try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
        try opt_buffer.drawTextBuffer(view, 0, 0);


        // At x=5, showing chars 5-24: " x = function(y) { "
        // Position 0: ' ' (source 5) - should be white
        // Position 5: 'f' (source 10) - should be GREEN
        const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, ' '), cell_0.char);
        try std.testing.expect(isWhite(cell_0.fg, epsilon));

        const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'f'), cell_5.char);
        try std.testing.expect(isGreen(cell_5.fg, epsilon));
    }

    // Test at x=15 (in middle of "function")
    {
        view.setViewport(.{ .x = 15, .y = 0, .width = 20, .height = 1 });
        var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
        defer opt_buffer.deinit();

        try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
        try opt_buffer.drawTextBuffer(view, 0, 0);


        // At x=15, showing chars 15-34: "ion(y) { return y * "
        // "const x = function..."
        //  0123456789012345678...
        // Position 0: 'i' (source 15) - should be GREEN (part of "function" 10-18)
        // Position 1: 'o' (source 16) - should be GREEN
        // Position 2: 'n' (source 17) - should be GREEN
        // Position 3: '(' (source 18) - should be WHITE (end of "function")
        const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'i'), cell_0.char);
        try std.testing.expect(isGreen(cell_0.fg, epsilon));

        const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'o'), cell_1.char);
        try std.testing.expect(isGreen(cell_1.fg, epsilon));

        const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'n'), cell_2.char);
        try std.testing.expect(isGreen(cell_2.fg, epsilon));

        const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, '('), cell_3.char);
        try std.testing.expect(isWhite(cell_3.fg, epsilon));

        // Position 9: 'r' (source 24) - should be BLUE (start of "return" 24-30)
        const cell_9 = opt_buffer.get(9, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'r'), cell_9.char);
        try std.testing.expect(isBlue(cell_9.fg, epsilon));
    }

    // Test at x=25 (past "return")
    {
        view.setViewport(.{ .x = 25, .y = 0, .width = 20, .height = 1 });
        var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
        defer opt_buffer.deinit();

        try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
        try opt_buffer.drawTextBuffer(view, 0, 0);


        // At x=25, showing chars 25-44: "eturn y * 2; }"
        // Position 0: 'e' (source 25) - should be BLUE (part of "return" 24-30)
        // Position 4: 'n' (source 29) - should be BLUE
        // Position 5: ' ' (source 30) - should be WHITE (end of "return")
        // Position 10: '2' (source 35) - should be YELLOW
        const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'e'), cell_0.char);
        try std.testing.expect(isBlue(cell_0.fg, epsilon));

        const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, 'n'), cell_4.char);
        try std.testing.expect(isBlue(cell_4.fg, epsilon));

        const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, ' '), cell_5.char);
        try std.testing.expect(isWhite(cell_5.fg, epsilon));

        const cell_10 = opt_buffer.get(10, 0) orelse unreachable;
        try std.testing.expectEqual(@as(u32, '2'), cell_10.char);
        try std.testing.expect(isYellow(cell_10.fg, epsilon));
    }
}

test "drawTextBuffer - selection with horizontal viewport offset" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    // Text: "0123456789ABCDEFGHIJ"
    // We'll set viewport to x=5, showing "56789ABCDE"
    // Then we'll select characters 7-12 (which are "789AB")
    // Expected: in the rendered buffer, "789AB" should be highlighted
    try tb.setText("0123456789ABCDEFGHIJ");

    view.setWrapMode(.none);
    view.setWrapWidth(null);
    view.setViewport(.{ .x = 5, .y = 0, .width = 10, .height = 1 });

    // Select characters at positions 7-12 in the original text ("789AB")
    view.setSelection(7, 12, RGBA{ 1.0, 1.0, 0.0, 1.0 }, RGBA{ 0.0, 0.0, 0.0, 1.0 });

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        10,
        1,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // The viewport shows positions 5-14 of the text
    // Characters 7-11 (0-indexed) should be highlighted
    // In the buffer:
    // Position 0: '5' - not highlighted
    // Position 1: '6' - not highlighted
    // Position 2: '7' - HIGHLIGHTED (char pos 7)
    // Position 3: '8' - HIGHLIGHTED (char pos 8)
    // Position 4: '9' - HIGHLIGHTED (char pos 9)
    // Position 5: 'A' - HIGHLIGHTED (char pos 10)
    // Position 6: 'B' - HIGHLIGHTED (char pos 11)
    // Position 7: 'C' - not highlighted (char pos 12, selection end is exclusive)
    // Position 8: 'D' - not highlighted
    // Position 9: 'E' - not highlighted

    const epsilon: f32 = 0.01;
    const yellow_bg = RGBA{ 1.0, 1.0, 0.0, 1.0 };

    // Check non-highlighted cells
    const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '5'), cell_0.char);
    const has_yellow_0 = @abs(cell_0.bg[0] - yellow_bg[0]) < epsilon and
        @abs(cell_0.bg[1] - yellow_bg[1]) < epsilon and
        @abs(cell_0.bg[2] - yellow_bg[2]) < epsilon;
    try std.testing.expect(!has_yellow_0);

    const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '6'), cell_1.char);
    const has_yellow_1 = @abs(cell_1.bg[0] - yellow_bg[0]) < epsilon and
        @abs(cell_1.bg[1] - yellow_bg[1]) < epsilon and
        @abs(cell_1.bg[2] - yellow_bg[2]) < epsilon;
    try std.testing.expect(!has_yellow_1);

    // Check highlighted cells
    const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '7'), cell_2.char);
    const has_yellow_2 = @abs(cell_2.bg[0] - yellow_bg[0]) < epsilon and
        @abs(cell_2.bg[1] - yellow_bg[1]) < epsilon and
        @abs(cell_2.bg[2] - yellow_bg[2]) < epsilon;
    try std.testing.expect(has_yellow_2);

    const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, '8'), cell_3.char);
    const has_yellow_3 = @abs(cell_3.bg[0] - yellow_bg[0]) < epsilon and
        @abs(cell_3.bg[1] - yellow_bg[1]) < epsilon and
        @abs(cell_3.bg[2] - yellow_bg[2]) < epsilon;
    try std.testing.expect(has_yellow_3);

    const cell_6 = opt_buffer.get(6, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'B'), cell_6.char);
    const has_yellow_6 = @abs(cell_6.bg[0] - yellow_bg[0]) < epsilon and
        @abs(cell_6.bg[1] - yellow_bg[1]) < epsilon and
        @abs(cell_6.bg[2] - yellow_bg[2]) < epsilon;
    try std.testing.expect(has_yellow_6);

    // Check cells after selection
    const cell_7 = opt_buffer.get(7, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, 'C'), cell_7.char);
    const has_yellow_7 = @abs(cell_7.bg[0] - yellow_bg[0]) < epsilon and
        @abs(cell_7.bg[1] - yellow_bg[1]) < epsilon and
        @abs(cell_7.bg[2] - yellow_bg[2]) < epsilon;
    try std.testing.expect(!has_yellow_7);
}

test "drawTextBuffer - Chinese text with wrapping no stray bytes" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const text =
        \\前后端分离 - TypeScript逻辑 + Go TUI界面
        \\组件化设计 - 基于tview的可复用组件
        \\渐进式交互 - 逐步披露避免信息过载
        \\智能上下文 - 基于项目状态动态生成问题
        \\丰富的问题类型 - 支持6种不同的交互形式
        \\完整的验证 - 实时输入验证和错误处理
    ;

    try tb.setText(text);

    // Try word wrapping with a width that might split multibyte chars
    view.setWrapMode(.word);
    view.setWrapWidth(35);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        40,
        20,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // Write the rendered buffer to check for stray bytes
    var out_buffer: [2000]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    // Verify the output is valid UTF-8
    try std.testing.expect(std.unicode.utf8ValidateSlice(result));

    // Verify that the original text is contained in the output (with possible spaces/newlines from wrapping)
    try std.testing.expect(std.mem.indexOf(u8, result, "完整的验证") != null);
    try std.testing.expect(std.mem.indexOf(u8, result, "实时输入验证和错误处理") != null);

    // Check specific problematic line - should NOT contain stray bytes
    // The line should be present correctly (possibly wrapped with spaces)
    // But there should be NO stray å character or partial UTF-8 sequences
    try std.testing.expect(std.mem.indexOf(u8, result, "å式") == null); // This should NOT appear
    try std.testing.expect(std.mem.indexOf(u8, result, "å") == null); // No stray partial bytes

    // Verify the problematic characters appear correctly
    try std.testing.expect(std.mem.indexOf(u8, result, "形式") != null);
}

test "drawTextBuffer - Chinese text WITHOUT wrapping no duplicate chunks" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const text =
        \\前后端分离 - TypeScript逻辑 + Go TUI界面
        \\组件化设计 - 基于tview的可复用组件
        \\渐进式交互 - 逐步披露避免信息过载
        \\智能上下文 - 基于项目状态动态生成问题
        \\丰富的问题类型 - 支持6种不同的交互形式
        \\完整的验证 - 实时输入验证和错误处理
    ;

    try tb.setText(text);

    // Word wrap mode but with wide width so nothing actually wraps
    view.setWrapMode(.word);
    view.setWrapWidth(80);
    view.updateVirtualLines();

    const vlines = view.getVirtualLines();

    // Check each virtual line - should have exactly ONE chunk when width is large enough
    for (vlines) |vline| {
        // Each line should have exactly ONE chunk when not actually wrapping
        try std.testing.expectEqual(@as(usize, 1), vline.chunks.items.len);
    }

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        80,
        10,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // Write the rendered buffer
    var out_buffer: [2000]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    // Verify the output is valid UTF-8
    try std.testing.expect(std.unicode.utf8ValidateSlice(result));

    // Should NOT contain stray bytes
    try std.testing.expect(std.mem.indexOf(u8, result, "å") == null);

    // All text should be present
    try std.testing.expect(std.mem.indexOf(u8, result, "完整的验证 - 实时输入验证和错误处理") != null);
}

test "drawTextBuffer - Chinese text with CHAR wrapping no stray bytes" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    const text =
        \\前后端分离 - TypeScript逻辑 + Go TUI界面
        \\组件化设计 - 基于tview的可复用组件
        \\渐进式交互 - 逐步披露避免信息过载
        \\智能上下文 - 基于项目状态动态生成问题
        \\丰富的问题类型 - 支持6种不同的交互形式
        \\完整的验证 - 实时输入验证和错误处理
    ;

    try tb.setText(text);

    // Char wrapping with a width that might split multibyte chars
    view.setWrapMode(.char);
    view.setWrapWidth(35);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        35,
        20,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // Write the rendered buffer to check for stray bytes
    var out_buffer: [2000]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    // Verify the output is valid UTF-8
    try std.testing.expect(std.unicode.utf8ValidateSlice(result));

    // Should NOT contain stray bytes
    try std.testing.expect(std.mem.indexOf(u8, result, "å") == null);

    // Verify the problematic characters appear correctly
    try std.testing.expect(std.mem.indexOf(u8, result, "形式") != null);
    try std.testing.expect(std.mem.indexOf(u8, result, "完整的验证") != null);
}

test "drawTextBuffer - word wrap CJK mixed text without break points" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("한글,English,中文,日本語,混合,Test,測試,テスト,가나다,ABC,一二三,あいう,라마바,DEF,四五六,えおか");

    view.setWrapMode(.word);
    view.setWrapWidth(20);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        30,
        20,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var out_buffer: [1000]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    const vlines = view.getVirtualLines();
    try std.testing.expect(vlines.len > 1);

    var y: u32 = 0;
    while (y < vlines.len) : (y += 1) {
        const first_cell = opt_buffer.get(0, y);
        if (first_cell) |cell| {
            try std.testing.expect(!gp.isContinuationChar(cell.char));
        }
    }

    try std.testing.expect(std.unicode.utf8ValidateSlice(result));
}

test "drawTextBuffer - word wrap CJK text preserves UTF-8 boundaries" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    try tb.setText("한글,English,中文,日本語,混合,Test,測試,テスト,가나다,ABC,一二三,あいう,라마바,DEF,四五六,えおか");

    view.setWrapMode(.word);
    view.setWrapWidth(20);
    view.updateVirtualLines();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        30,
        20,
        .{ .pool = pool, .width_method = .unicode },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    var out_buffer: [1000]u8 = undefined;
    const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
    const result = out_buffer[0..written];

    try std.testing.expect(std.unicode.utf8ValidateSlice(result));
    try std.testing.expect(std.mem.indexOf(u8, result, "ä") == null);

    var i: usize = 0;
    while (i < result.len) : (i += 1) {
        if (result[i] == 0xE4) {
            if (i + 1 >= result.len) {
                return error.TestFailed;
            }
            const next_byte = result[i + 1];
            if (next_byte < 0x80 or next_byte > 0xBF) {
                return error.TestFailed;
            }
        }
    }

    const vlines = view.getVirtualLines();
    var y: u32 = 0;
    while (y < vlines.len) : (y += 1) {
        const first_cell = opt_buffer.get(0, y);
        if (first_cell) |cell| {
            try std.testing.expect(!gp.isContinuationChar(cell.char));
        }
    }
}

test "drawTextBuffer - wcwidth mode does not render ZWJ or VS16 as characters" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    var tb = try TextBuffer.init(std.testing.allocator, pool, .wcwidth);
    defer tb.deinit();

    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    // Test comprehensive emoji with ZWJ and variation selectors
    const test_string = "👩🏽‍💻  👨‍👩‍👧‍👦  🏳️‍🌈  🇺🇸  🇩🇪  🇯🇵  🇮🇳";
    try tb.setText(test_string);

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        50,
        5,
        .{ .pool = pool, .width_method = .wcwidth },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // Verify the raw bytes contain ZWJ
    var text_buf: [200]u8 = undefined;
    const text_len = tb.getPlainTextIntoBuffer(&text_buf);
    const text_bytes = text_buf[0..text_len];

    // Check that ZWJ (0xE2 0x80 0x8D) is in the original text
    var has_zwj = false;
    var zwj_count: u32 = 0;
    var i: usize = 0;
    while (i + 2 < text_len) : (i += 1) {
        if (text_bytes[i] == 0xE2 and text_bytes[i + 1] == 0x80 and text_bytes[i + 2] == 0x8D) {
            has_zwj = true;
            zwj_count += 1;
        }
    }
    try std.testing.expect(has_zwj);
    try std.testing.expect(zwj_count >= 5); // Should have multiple ZWJs

    // Now check the rendered buffer - ZWJ should NOT appear as rendered characters
    var render_buf: [500]u8 = undefined;
    const render_len = try opt_buffer.writeResolvedChars(&render_buf, false);
    _ = render_len;

    // Check positions where we expect actual characters
    // Woman technologist: 👩🏽‍💻
    // Expected positions in wcwidth mode:
    // Col 0-1: 👩 (woman, width 2)
    // Col 2-3: 🏽 (skin tone, width 2)
    // ZWJ should be SKIPPED in rendering (not take a cell)
    // Col 4-5: 💻 (laptop, width 2)
    // Col 6-7: spaces

    const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_0.char));

    // Get the actual bytes for this grapheme
    const gid_0 = gp.graphemeIdFromChar(cell_0.char);
    const grapheme_bytes_0 = pool.get(gid_0) catch unreachable;

    // Woman emoji should be F0 9F 91 A9 (4 bytes)
    try std.testing.expectEqual(@as(usize, 4), grapheme_bytes_0.len);
    try std.testing.expectEqual(@as(u8, 0xF0), grapheme_bytes_0[0]);
    try std.testing.expectEqual(@as(u8, 0x9F), grapheme_bytes_0[1]);
    try std.testing.expectEqual(@as(u8, 0x91), grapheme_bytes_0[2]);
    try std.testing.expectEqual(@as(u8, 0xA9), grapheme_bytes_0[3]);

    const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expect(gp.isContinuationChar(cell_1.char));

    const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_2.char));

    const gid_2 = gp.graphemeIdFromChar(cell_2.char);
    const grapheme_bytes_2 = pool.get(gid_2) catch unreachable;

    // Skin tone should be F0 9F 8F BD (4 bytes) - NO ZWJ
    try std.testing.expectEqual(@as(usize, 4), grapheme_bytes_2.len);
    try std.testing.expectEqual(@as(u8, 0xF0), grapheme_bytes_2[0]);
    try std.testing.expectEqual(@as(u8, 0x9F), grapheme_bytes_2[1]);
    try std.testing.expectEqual(@as(u8, 0x8F), grapheme_bytes_2[2]);
    try std.testing.expectEqual(@as(u8, 0xBD), grapheme_bytes_2[3]);

    const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
    try std.testing.expect(gp.isContinuationChar(cell_3.char));

    // Col 4 should be laptop emoji (ZWJ was NOT rendered)
    const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_4.char));

    const gid_4 = gp.graphemeIdFromChar(cell_4.char);
    const grapheme_bytes_4 = pool.get(gid_4) catch unreachable;

    // Laptop should be F0 9F 92 BB (4 bytes) - NO ZWJ
    try std.testing.expectEqual(@as(usize, 4), grapheme_bytes_4.len);
    try std.testing.expectEqual(@as(u8, 0xF0), grapheme_bytes_4[0]);
    try std.testing.expectEqual(@as(u8, 0x9F), grapheme_bytes_4[1]);
    try std.testing.expectEqual(@as(u8, 0x92), grapheme_bytes_4[2]);
    try std.testing.expectEqual(@as(u8, 0xBB), grapheme_bytes_4[3]);

    // Verify none of the rendered graphemes contain ZWJ bytes
    var cell_idx: u32 = 0;
    while (cell_idx < 50) : (cell_idx += 1) {
        if (opt_buffer.get(cell_idx, 0)) |cell| {
            if (gp.isGraphemeChar(cell.char)) {
                const gid = gp.graphemeIdFromChar(cell.char);
                const grapheme_bytes = pool.get(gid) catch continue;

                // Check this grapheme doesn't contain ZWJ
                var j: usize = 0;
                while (j + 2 < grapheme_bytes.len) : (j += 1) {
                    if (grapheme_bytes[j] == 0xE2 and grapheme_bytes[j + 1] == 0x80 and grapheme_bytes[j + 2] == 0x8D) {
                        return error.TestFailed;
                    }
                }

                // Also check for VS16 (0xEF 0xB8 0x8F)
                j = 0;
                while (j + 2 < grapheme_bytes.len) : (j += 1) {
                    if (grapheme_bytes[j] == 0xEF and grapheme_bytes[j + 1] == 0xB8 and grapheme_bytes[j + 2] == 0x8F) {
                        return error.TestFailed;
                    }
                }
            }
        }
    }
}

test "drawTextBuffer - wcwidth cursor movement matches rendered output" {
    const pool = gp.initGlobalPool(std.testing.allocator);
    defer gp.deinitGlobalPool();

    const edit_buffer = @import("../edit-buffer.zig");
    const EditBuffer = edit_buffer.EditBuffer;

    var eb = try EditBuffer.init(std.testing.allocator, pool, .wcwidth);
    defer eb.deinit();

    // Use the exact text from user's request
    const test_text = "👩🏽‍💻  👨‍👩‍👧‍👦";
    try eb.setText(test_text, false);

    // Create a text buffer view and render it
    const tb = eb.getTextBuffer();
    var view = try TextBufferView.init(std.testing.allocator, tb);
    defer view.deinit();

    var opt_buffer = try OptimizedBuffer.init(
        std.testing.allocator,
        30,
        5,
        .{ .pool = pool, .width_method = .wcwidth },
    );
    defer opt_buffer.deinit();

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);

    // Get what was actually rendered
    var render_buf: [200]u8 = undefined;
    const render_len = try opt_buffer.writeResolvedChars(&render_buf, false);
    _ = render_len;

    // Expected rendering in wcwidth mode:
    // Col 0-1: 👩 (woman, width 2)
    // Col 2-3: 🏽 (skin, width 2)
    // Col 4-5: 💻 (laptop, width 2) - ZWJ filtered out
    // Col 6: space (width 1)
    // Col 7: space (width 1)
    // Col 8-9: 👨 (man, width 2)
    // Col 10-11: 👩 (woman, width 2) - ZWJ filtered out
    // Col 12-13: 👧 (girl, width 2) - ZWJ filtered out
    // Col 14-15: 👦 (boy, width 2) - ZWJ filtered out

    // Verify rendered cells match expectations

    // Col 0: Woman emoji start
    const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_0.char));
    const gid_0 = gp.graphemeIdFromChar(cell_0.char);
    const bytes_0 = pool.get(gid_0) catch unreachable;
    try std.testing.expectEqualSlices(u8, &[_]u8{ 0xF0, 0x9F, 0x91, 0xA9 }, bytes_0);

    // Col 1: Woman continuation
    const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
    try std.testing.expect(gp.isContinuationChar(cell_1.char));

    // Col 2: Skin tone emoji start
    const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_2.char));
    const gid_2 = gp.graphemeIdFromChar(cell_2.char);
    const bytes_2 = pool.get(gid_2) catch unreachable;
    try std.testing.expectEqualSlices(u8, &[_]u8{ 0xF0, 0x9F, 0x8F, 0xBD }, bytes_2);

    // Col 3: Skin continuation
    const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
    try std.testing.expect(gp.isContinuationChar(cell_3.char));

    // Col 4: Laptop emoji start (ZWJ was filtered)
    const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_4.char));
    const gid_4 = gp.graphemeIdFromChar(cell_4.char);
    const bytes_4 = pool.get(gid_4) catch unreachable;
    try std.testing.expectEqualSlices(u8, &[_]u8{ 0xF0, 0x9F, 0x92, 0xBB }, bytes_4);

    // Col 5: Laptop continuation
    const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
    try std.testing.expect(gp.isContinuationChar(cell_5.char));

    // Col 6-7: Spaces
    const cell_6 = opt_buffer.get(6, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_6.char);
    const cell_7 = opt_buffer.get(7, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_7.char);

    // Col 8: Man emoji start
    const cell_8 = opt_buffer.get(8, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_8.char));
    const gid_8 = gp.graphemeIdFromChar(cell_8.char);
    const bytes_8 = pool.get(gid_8) catch unreachable;
    try std.testing.expectEqualSlices(u8, &[_]u8{ 0xF0, 0x9F, 0x91, 0xA8 }, bytes_8);

    // Col 10: Woman emoji start (ZWJ filtered)
    const cell_10 = opt_buffer.get(10, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_10.char));
    const gid_10 = gp.graphemeIdFromChar(cell_10.char);
    const bytes_10 = pool.get(gid_10) catch unreachable;
    try std.testing.expectEqualSlices(u8, &[_]u8{ 0xF0, 0x9F, 0x91, 0xA9 }, bytes_10);

    // Col 12: Girl emoji start (ZWJ filtered)
    const cell_12 = opt_buffer.get(12, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_12.char));
    const gid_12 = gp.graphemeIdFromChar(cell_12.char);
    const bytes_12 = pool.get(gid_12) catch unreachable;
    try std.testing.expectEqualSlices(u8, &[_]u8{ 0xF0, 0x9F, 0x91, 0xA7 }, bytes_12);

    // Col 14: Boy emoji start (ZWJ filtered)
    const cell_14 = opt_buffer.get(14, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_14.char));
    const gid_14 = gp.graphemeIdFromChar(cell_14.char);
    const bytes_14 = pool.get(gid_14) catch unreachable;
    try std.testing.expectEqualSlices(u8, &[_]u8{ 0xF0, 0x9F, 0x91, 0xA6 }, bytes_14);

    // Now test that cursor movement matches the rendered cells
    try eb.setCursor(0, 0);
    var cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 0), cursor.col);

    // Move through woman technologist
    eb.moveRight(); // Woman at col 0-1
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 2), cursor.col);

    eb.moveRight(); // Skin at col 2-3
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 4), cursor.col);

    eb.moveRight(); // Laptop at col 4-5 (ZWJ skipped)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 6), cursor.col);

    eb.moveRight(); // First space at col 6
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 7), cursor.col);

    eb.moveRight(); // Second space at col 7
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 8), cursor.col);

    // Move through family emoji
    eb.moveRight(); // Man at col 8-9
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 10), cursor.col);

    eb.moveRight(); // Woman at col 10-11 (ZWJ skipped)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 12), cursor.col);

    eb.moveRight(); // Girl at col 12-13 (ZWJ skipped)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 14), cursor.col);

    eb.moveRight(); // Boy at col 14-15 (ZWJ skipped)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 16), cursor.col);

    // Move back through the same cells
    eb.moveLeft(); // Back to col 14 (before boy)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 14), cursor.col);

    eb.moveLeft(); // Back to col 12 (before girl, ZWJ skipped)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 12), cursor.col);

    eb.moveLeft(); // Back to col 10 (before woman, ZWJ skipped)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 10), cursor.col);

    eb.moveLeft(); // Back to col 8 (before man, ZWJ skipped)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 8), cursor.col);

    eb.moveLeft(); // Back to col 7 (space 2)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 7), cursor.col);

    eb.moveLeft(); // Back to col 6 (space 1)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 6), cursor.col);

    eb.moveLeft(); // Back to col 4 (before laptop, ZWJ skipped)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 4), cursor.col);

    eb.moveLeft(); // Back to col 2 (before skin)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 2), cursor.col);

    eb.moveLeft(); // Back to col 0 (before woman)
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 0), cursor.col);

    // Move to end and backspace through everything
    try eb.setCursor(0, 16); // At the end
    cursor = eb.getPrimaryCursor();

    // Get initial rendered state
    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
    var buf: [200]u8 = undefined;
    var buf_len = try opt_buffer.writeResolvedChars(&buf, false);

    // Backspace boy (col 14-15)
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 14), cursor.col);

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
    buf_len = try opt_buffer.writeResolvedChars(&buf, false);

    // Verify boy is gone but girl is still there
    const cell_14_after = opt_buffer.get(14, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_14_after.char);
    const cell_12_still = opt_buffer.get(12, 0) orelse unreachable;
    try std.testing.expect(gp.isGraphemeChar(cell_12_still.char));

    // Backspace girl (col 12-13), ZWJ auto-deleted
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 12), cursor.col);

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
    buf_len = try opt_buffer.writeResolvedChars(&buf, false);

    // Backspace woman (col 10-11), ZWJ auto-deleted
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 10), cursor.col);

    // Backspace man (col 8-9), ZWJ auto-deleted
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 8), cursor.col);

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
    buf_len = try opt_buffer.writeResolvedChars(&buf, false);

    // Should have just "👩🏽💻  " remaining (woman technologist + 2 spaces)
    // Verify man is gone
    const cell_8_after = opt_buffer.get(8, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_8_after.char);

    // Backspace space 2
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 7), cursor.col);

    // Backspace space 1
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 6), cursor.col);

    // Backspace laptop (col 4-5), ZWJ auto-deleted
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 4), cursor.col);

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
    buf_len = try opt_buffer.writeResolvedChars(&buf, false);

    // Backspace skin+ZWJ (col 2-3)
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 2), cursor.col);

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
    buf_len = try opt_buffer.writeResolvedChars(&buf, false);

    // Backspace woman (col 0-1)
    try eb.backspace();
    cursor = eb.getPrimaryCursor();
    try std.testing.expectEqual(@as(u32, 0), cursor.col);

    try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
    try opt_buffer.drawTextBuffer(view, 0, 0);
    buf_len = try opt_buffer.writeResolvedChars(&buf, false);

    // All cells should now be spaces
    const cell_0_final = opt_buffer.get(0, 0) orelse unreachable;
    try std.testing.expectEqual(@as(u32, ' '), cell_0_final.char);
}
